package com.lost.octopus.es.helper.entity.wrapper;

import com.lost.octopus.es.helper.annotation.EsSearch;
import com.lost.octopus.es.helper.constant.ParameterConstant;
import com.lost.octopus.es.helper.entity.model.FieldConfig;
import com.lost.octopus.es.helper.util.ArrayUtil;
import com.lost.octopus.es.helper.util.StringUtil;
import lombok.extern.log4j.Log4j2;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * 范围查询，值为字符串，逗号隔开,必须存在逗号
 * 默认全包含查询 {@link #EQ_AND_EQ}，如需自定义，则配置{@link EsSearch#otherParam()} 选项 eq,not_eq 逗号隔开
 * 同sql  and ( xx>=v[0] and xx<=v[1] )
 * <br/>
 * 注解：{@code @EsSearch(queryType = EsQueryRangeWrapper.class,otherParam = EsQueryRangeWrapper.NOT_EQ_AND_EQ)}
 * <br/>
 * 示例：{@code "1,2"}
 * <br/>
 * 效果：{@code {"bool":{"must":[{"range":{"id":{"from":"1","to":"2","include_lower":false,"include_upper":true}}}]}} }
 * @author zhangbh
 **/
@Log4j2
@Component
public class EsQueryRangeWrapper extends AbstractQueryWrapper {

    private static final String EQ = "eq";
    private static final String NOT_EQ = "not_eq";

    public static final String EQ_AND_EQ = EQ + ParameterConstant.SEPARATOR + EQ;
    public static final String EQ_AND_NOT_EQ = EQ + ParameterConstant.SEPARATOR + NOT_EQ;
    public static final String NOT_EQ_AND_EQ = NOT_EQ + ParameterConstant.SEPARATOR + EQ;
    public static final String NOT_EQ_AND_NOT_EQ = NOT_EQ + ParameterConstant.SEPARATOR + NOT_EQ;

    @Resource
    private EsQueryBlankWrapper esQueryBlankWrapper;

    @Override
    public QueryBuilder addWrapper(BoolQueryBuilder builder, FieldConfig field) {

        String fieldName = field.getName();

        Object value = field.getValue();
        // 获取值
        List<Object> values = getValues(value, fieldName, field);
        if (CollectionUtils.isEmpty(values)) {
            return null;
        }
        // 获取范围查询类型
        String rangeType = getRangeType(field);
        BoolQueryBuilder rangeQuery = QueryBuilders.boolQuery();
        // 范围查询
        RangeQueryBuilder rangeBuilder = addRangeParam(fieldName, values, rangeType);
        if (rangeBuilder == null) {
            return rangeQuery;
        }
        rangeQuery.must(rangeBuilder);

        // 字段必须存在，不能为空
        FieldConfig copy = field.copy("");
        copy.setValue(EsQueryBlankWrapper.NOT_BLANK_SEARCH_KEY);
        QueryBuilder notNullQuery = esQueryBlankWrapper.addWrapper(QueryBuilders.boolQuery(), copy);
        // 拼装起来（需要满足两个条件），方便外部复用
        if (notNullQuery != null) {
            rangeQuery.must(notNullQuery);
        }

        useMode(builder, rangeBuilder, getMode(field));
        return rangeQuery;
    }

    /**
     * 取值,支持数组、列表，或者逗号隔开的两位数，超过无用
     * 如果一个值，就是默认是start
     * 否则开始需要[null,xx]或者["",xx]或者 ,xx
     * @param value     值
     * @param fieldName 字段名
     * @return 值
     */
    protected List<Object> getValues(Object value, String fieldName, FieldConfig field) {
        List<Object> values = ArrayUtil.transList(value);
        if (CollectionUtils.isEmpty(values)) {
            return new ArrayList<>();
        }
        return values;
    }

    /**
     * 获取范围查询类型
     * @param field 查询对象
     * @return 类型
     */
    protected String getRangeType(FieldConfig field) {
        // 定制化查询条件,有默认值 EQ_AND_EQ
        String rangeType = field.getOtherParam();
        if (!StringUtils.hasText(rangeType)) {
            rangeType = EQ_AND_EQ;
        }
        return rangeType;
    }

    /**
     * 计算增加范围参数条件
     * @param fieldName 字段名
     * @param values    值，长度为2
     * @param rangeType 定制化参数，逗号隔开，选项[eq,not_eq]
     */
    @Nullable
    protected RangeQueryBuilder addRangeParam(String fieldName, List<Object> values, String rangeType) {
        // 多个条件，判断生成查询
        Object start = values.get(0);
        Object end = values.size() > 1 ? values.get(1) : null;
        // 校验合法性
        if (StringUtils.hasText(rangeType) && !rangeType.contains(ParameterConstant.SEPARATOR)) {
            throw new IllegalArgumentException("EsBoolQueryRangeWrapper 配置了扩展参数需要逗号隔开");
        }
        String[] eqs = rangeType.split(ParameterConstant.SEPARATOR);
        validVal(eqs[0]);
        validVal(eqs[1]);

        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery(fieldName);
        // 都为空则返回空
        if (StringUtil.isBlank(start) && StringUtil.isBlank(end)) {
            return null;
        }
        if (!StringUtil.isBlank(start)) {
            log.debug("es 查询{}范围起始,值为={}", fieldName, start);
            if (useEq(eqs[0])) {
                rangeQuery.gte(start);
            } else {
                rangeQuery.gt(start);
            }
        }
        if (!StringUtil.isBlank(end)) {
            log.debug("es 查询{}范围截止,值为={}", fieldName, end);
            if (useEq(eqs[1])) {
                rangeQuery.lte(end);
            } else {
                rangeQuery.lt(end);
            }
        }
        return rangeQuery;
    }

    /**
     * 检查是否是相等查询
     * @param eqSign 匹配标记
     * @return 是否是相等查询
     */
    private boolean useEq(String eqSign) {
        return !StringUtils.hasText(eqSign) || EQ.equals(eqSign);
    }

    /**
     * 校验是否是规范内的字符串
     * @param eqSign 匹配标记
     */
    private void validVal(String eqSign) {
        if (!StringUtils.hasText(eqSign) || EQ.equals(eqSign) || NOT_EQ.equals(eqSign)) {
            return;
        }
        throw new IllegalArgumentException("EsBoolQueryRangeWrapper 模式 otherParam值配置错误");
    }
}
